﻿using log4net;
using Newtonsoft.Json.Linq;
using per.esmool.utils;
using System;
using System.Collections.Generic;

namespace FlowSimulate {

    public class TaskMaker {

        private static readonly ILog LOG = LogManager.GetLogger(typeof(TaskMaker));

        readonly Random rand = new();

        public Suit Suit { get; set; }

        public float MinTaskCost { get; set; } = 1.0f;

        public float MaxTaskCost { get; set; } = 5.0f;

        public List<Task> CreateTasks() {
            LOG.Info("Creating tasks");
            var modules = this.Suit.Modules;
            var stages = this.Suit.Flows;
            var costs = this.Suit.CostReader;
            var list = new List<Task>();
            var id = 0;

            for (int i=0; i<modules.Count; i++) {
                var module = modules[i];
                var stageTasks = new Dictionary<string, List<int>>();
                for (int j=0; j<stages.Count; j++) {
                    var stage = stages[j];
                    var noTask = JsonKit.GetValue(stage, "noTask", false);
                    if (noTask) continue;
                    var cost = costs[i, j];
                    while (cost > 0) {
                        var taskCost = cost < this.MinTaskCost ?
                            cost :
                            (float)(this.rand.NextDouble() * (cost - this.MinTaskCost) + this.MinTaskCost);
                        if (taskCost > this.MaxTaskCost) continue;
                        
                        var taskKey = stage.Value<string>("key");
                        var follows = stage.Value<JArray>("follows");
                        
                        var after = FindPredecessorTaskIDs(stageTasks, follows, taskKey);
                        var task = CreateTask(module, stage, taskCost, id++, after);

                        cost -= taskCost;
                        list.Add(task);

                        if (!stageTasks.ContainsKey(taskKey))
                            stageTasks.Add(taskKey, []);
                        stageTasks[taskKey].Add(task.ID);
                    }
                }
            }

            LOG.Info($"Total created {list.Count} tasks");
            return list;
        }

        private static List<int> FindPredecessorTaskIDs(Dictionary<string, List<int>> taskIds, JArray follows, string selfKey) {
            var list = new List<int>();
            foreach (var item in follows) {
                var stage = item.Value<string>();
                if (!taskIds.ContainsKey(stage))
                    continue;
                var predecessors = taskIds[stage];
                list.AddRange(predecessors);
            }
            if (!taskIds.ContainsKey(selfKey))
                return list;
            var selfPredecssors = taskIds[selfKey];
            list.AddRange(selfPredecssors);
            return list;
        }

        private static Task CreateTask(JObject module, JObject stage, float cost, int id, List<int> after) {
            Task task = new() {
                ID = id,
                Module = module.Value<string>("key"),
                Stage = stage.Value<string>("key"),
                Cost = cost,
                After = after,
            };
            var moduleName = module.Value<string>("name");
            var stageName = stage.Value<string>("name");
            LOG.Debug($"Created stage {stageName} for task {moduleName} with cost {cost:0.0f}w");

            return task;
        }
    }

    public class Task {

        public Task() { }

        public Task(Task from) {
            this.ID = from.ID;
            this.Module = from.Module;
            this.Stage = from.Stage;
            this.Cost = from.Cost;
            this.After = [.. from.After];
        }

        public int ID { get; set; }
        public string Module { get; set; }
        public string Stage { get; set; }
        public float Cost { get; set; }
        public List<int> After { get; set; }
    }

}
